const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');

class FileWatcherManager {
    constructor(app) {
        this.app = app;
        this.watchers = {};
    }

    // Function to read the entire file content
    readFileContent(filePath) {
        return new Promise((resolve, reject) => {
            fs.readFile(filePath, 'utf8', (err, data) => {
                if (err) reject(err);
                resolve(data);
            });
        });
    }

    // Function to read the new content added to the file
    readNewContent(filePath, lastSize) {
        return new Promise((resolve, reject) => {
            fs.stat(filePath, (err, stats) => {
                if (err) reject(err);
                if (stats.size > lastSize) {
                    const stream = fs.createReadStream(filePath, {
                        start: lastSize,
                        end: stats.size
                    });

                    let newData = '';
                    stream.on('data', (chunk) => {
                        newData += chunk;
                    });

                    stream.on('end', () => {
                        resolve({ newSize: stats.size, newData: newData });
                    });

                    stream.on('error', (error) => {
                        reject(error);
                    });
                } else {
                    resolve({ newSize: stats.size, newData: '' });
                }
            });
        });
    }

    notifyMessage(taskId, message) {
        // console.log(JSON.stringify({ message }));
        // this.app.server && this.app.server.broadcast(JSON.stringify(message));
        this.app.queue.enqueue(taskId, message);
    }

    // Function to add watcher for a directory
    addWatcher(dir, taskId, jsproxy, train_cfg) {
        console.log(`addWatcher dir = ${dir}`);
        if (this.watchers[dir]) {
            this.notifyMessage(taskId, {
                event: 'result',
                params: {
                    code: -1,
                    msg: 'Already watching directory: ${dir}'
                }
            });
            return;
        }

        const fileSizes = {};
        const watcher = chokidar.watch(dir, { persistent: true });

        this.watchers[dir] = {
            taskId: taskId,
            watcher: watcher
        };
        let whitelist = ['.py', '.log', '.json'];
        watcher
            .on('add', async (filePath) => {
                // console.log(`File ${filePath} has been added`);
                // TODO: 对于像模型的大文件，需要优化处理方式
                let extname = path.extname(filePath);
                if (whitelist.includes(extname)) {
                    try {
                        const data = await this.readFileContent(filePath);
                        let result = jsproxy.loadRuntimeLogs('running', filePath, data, train_cfg);
                        if (result) {
                            this.notifyMessage(taskId, result);
                        }
                        fileSizes[filePath] = data.length;
                    } catch (error) {
                        console.error('Error reading new file:', error);
                    }
                } else {
                    // console.log(`File ${filePath} has been added, but filter by whitelist!`);
                }
            })
            .on('change', async (filePath) => {
                // console.log(`File ${filePath} has been changed`);
                let extname = path.extname(filePath);
                if (whitelist.includes(extname)) {
                    try {
                        const { newSize, newData } = await this.readNewContent(filePath, fileSizes[filePath]);
                        if (newData) {
                            let result = jsproxy.loadRuntimeLogs('running', filePath, newData, train_cfg);
                            if (result) {
                                this.notifyMessage(taskId, result);
                            }
                        }
                        fileSizes[filePath] = newSize;
                    } catch (error) {
                        console.error('Error reading changed file:', error);
                    }
                } else {
                    // console.log(`File ${filePath} has been changed, but filter by whitelist!`);
                }
            })
            .on('unlink', (filePath) => {
                // console.log(`File ${filePath} has been removed`);
                delete fileSizes[filePath];
            })
            .on('addDir', (dirPath) => {
                // console.log(`Directory ${dirPath} has been added`);
            })
            .on('unlinkDir', (dirPath) => {
                // console.log(`Directory ${dirPath} has been removed`);
            });
    }

    // Function to remove watcher for a directory
    removeWatcher(dir) {
        const watcher = this.watchers[dir].watcher;
        if (watcher) {
            watcher.close();
            delete this.watchers[dir];
            // this.notifyMessage({ message: `Stopped watching directory: ${dir}` });
        } else {
            // this.notifyMessage({ message: `Directory not being watched: ${dir}` });
        }
    }

    // Function to stop all watchers
    stopAllWatchers() {
        Object.keys(this.watchers).forEach((dir) => {
            this.watchers[dir].watcher.close();
            delete this.watchers[dir];
        });
        console.log(`## Close File/Directory Monitors OK!`);
    }
}

module.exports = { FileWatcherManager };
